package tv.geir.slackspace.space;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import net.jini.core.lease.Lease;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import tv.geir.slackspace.space.ObjectSpace.Entry;

public class ObjectGeneration implements Runnable{
	
	private static Log log = LogFactory.getLog( ObjectGeneration.class );
	
	private int generation;
	private List<Entry> currentGeneration;
	private List<Entry> nextGeneration;
	private long sleepTime;
	private boolean running;
	
	public ObjectGeneration( List<Entry> currentGeneration, List<Entry> nextGeneration, long sleepTime, int generation ) {
		this.generation = generation;
		this.currentGeneration = currentGeneration;
		this.nextGeneration = nextGeneration;
		this.sleepTime = sleepTime;
	}
	
	public void run() {
		while( running ) {
			try {							
				Thread.sleep( sleepTime );
			} catch (InterruptedException e) {						
				if( !running ) {
					log.info( "Shutting down LeaseExpirationManager" );
					return;
				}
			}
			
			List<Entry> expiredEntries = removeExpiredObjects( currentGeneration );
			if( expiredEntries != null ) {
				int count = expiredEntries.size();
				int seconds = (int) ( sleepTime / 1000 ) ;
				log.debug( count + " entries expired." );
				log.debug( "Waiting " + seconds + "s for next lease expired cleanup" );
			}
			
			// if there is a next generation, move the surviving entries to that generation
			if( nextGeneration != null ) {
				List<Entry> survivingEntries = new ArrayList<Entry>();
				synchronized ( currentGeneration ) {
					Collections.copy( survivingEntries, currentGeneration );
					currentGeneration.removeAll( survivingEntries );
				}
				synchronized ( nextGeneration ) {
					nextGeneration.addAll( survivingEntries );
				}				
			}
		}
		
	}

	private List<Entry> removeExpiredObjects( List<Entry> objects) {
		if( objects.size() == 0 ) {
			// escape if no objects in pool
			if( log.isDebugEnabled() ) {
				log.debug( "No objects in pool, skipping removal" );
			}
			return null ;
		}
		LinkedList<Entry> expired = new LinkedList<Entry>();
		synchronized ( objects ) {		
			for( Entry e : objects ) {
				long now = System.currentTimeMillis();
				if( e.lease.getLeaseTime() != Lease.FOREVER &&
					e.lease.getLeaseTime() < now ) {
					if(log.isDebugEnabled() ) {
						log.debug( "Entry expired: " + e );
					}
					expired.add( e );
					// TODO add notification to removal?
				}
			}
			
			for( Entry r : expired ) {
				objects.remove( r );
			}
		}
		return expired;
	}
}
